package dataStructure;

import dataStructure.interfaces.Node;

import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.StringJoiner;
import java.util.function.Consumer;

public abstract class AbstractNode<E> implements Node<E>{
    private E element;
    private Node<E> next;

    @Override
    public E getElement(){
        return element;
    }

    @Override
    public boolean setElement(E e) {
        this.element = e;

        return true;
    }

    @Override
    public boolean setNextElement(E e) {
        if (hasNext()){
            getNext().setElement(e);

            return true;
        }

        return false;
    }

    @Override
    public Node<E> getNext() {
        return next;
    }

    @Override
    public E getNextElement() {
        if (hasNext()){
            return next.getElement();
        }

        throw new UnsupportedOperationException();
    }

    @Override
    public Node<E> getNextNext() {
        if (hasNext()){
            return getNext().getNext();
        }

        return null;
    }

    @Override
    public boolean hasNext(){
        return Objects.nonNull(getNext());
    }

    @Override
    public Node<E> connectNext(Node<E> nextNode) {
        this.next = nextNode;

        return hasNext() ? nextNode : this;
    }

    @Override
    public Node<E> connectPrevious(Node<E> previousNode) {
        if (Objects.nonNull(previousNode)){
            previousNode.connectNext(this);

            return previousNode;
        }

        return this;
    }

    @Override
    public Node<E> deleteNext() {
        Node<E> deleteNode = getNext();

        if (Objects.nonNull(deleteNode)){
            connectNext(null);
        }

        return deleteNode;
    }

    @Override
    public Node<E> removeNext() {
        Node<E> deleteNode = getNext();

        if (Objects.nonNull(deleteNode)){
            connectNext(deleteNode.getNext());
            deleteNode.connectNext(null);
        }

        return deleteNode;
    }

    @Override
    public boolean insertNext(Node<E> insertNode) {
        if (hasNext()){
            this.getNext().connectPrevious(insertNode);
        }

        this.connectNext(insertNode);

        return true;
    }

    @Override
    public String toDeepString() {
        StringJoiner sj = new StringJoiner("->");
        forEach(e -> sj.add(e.toString()));

        return sj.toString();
    }

    @Override
    public String toString() {
        return getElement().toString();
    }

    @Override
    public void forEach(Consumer<? super Node<E>> action) {
        Node<E> cursor = this;

        do {
            action.accept(cursor);
            cursor = cursor.getNext();
        }while (Objects.nonNull(cursor)); // if cursor has next node
    }

    @Override
    public Iterator<Node<E>> iterator() {
        return null;
    }

    @Override
    public Spliterator<Node<E>> spliterator() {
        return null;
    }

    abstract class AbstractNodeIterator<E> implements Iterator<E>{
        @Override
        public void remove() {
            Iterator.super.remove();
        }

        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Iterator.super.forEachRemaining(action);
        }

        @Override
        public boolean hasNext() {
            return AbstractNode.this.hasNext();
        }

        @Override
        public E next() {
            return null;
        }
    }
}
